package com.je.gateway.router.dispatcher;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.base.Strings;
import com.je.gateway.router.GlobalRouterParams;
import com.je.gateway.router.model.urlRouter.UrlDetail;
import com.netflix.config.DynamicPropertyFactory;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import org.apache.servicecomb.edge.core.EdgeInvocation;
import org.apache.servicecomb.edge.core.Utils;
import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UrlEdgeDispatcher extends GatewayAbstractRestDispatcher {

    private static final Logger logger = LoggerFactory.getLogger(UrlEdgeDispatcher.class);

    public static final String CONFIGURATION_ITEM = "URLMappedConfigurationItem";

    private static final String KEY_ENABLED = "servicecomb.http.dispatcher.edge.urlRouter.enabled";

    public UrlEdgeDispatcher() {
        if (this.enabled()) {
            loadConfigurations();
        }
    }

    @Override
    public void init(Router router) {
        String pattern = GlobalRouterParams.GLOBAL_URL_ROUTER.getPath();
        router.routeWithRegex(pattern).failureHandler(this::onFailure)
                .handler(this::preCheck)
                .handler(createBodyHandler())
                .handler(this::onRequest);
    }

    protected void preCheck(RoutingContext context) {
        UrlDetail urlDetail = findConfigurationItem(context.request().path());
        if (urlDetail == null) {
            // by pass body handler flag
            context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.TRUE);
            context.next();
            return;
        }
        context.put(CONFIGURATION_ITEM, urlDetail);
        context.next();
    }

    protected void onRequest(RoutingContext context) {
        UrlDetail urlDetail = context.get(CONFIGURATION_ITEM);
        if (isGateWayPath(context, context.request().path())) {
            context.next();
            return;
        }
        Boolean bypass = context.get(RestBodyHandler.BYPASS_BODY_HANDLER);
        if (Boolean.TRUE.equals(bypass)) {
            // clear flag
            context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.FALSE);
            context.next();
            return;
        }

        if (urlDetail == null) {
            context.response().setStatusCode(404).end("error matching param url");
            return;
        }

        String path;
        if (!Strings.isNullOrEmpty(urlDetail.getRealPath())) {
            path = urlDetail.getRealPath();
        } else {
            path = Utils.findActualPath(context.request().path(), urlDetail.getPrefixSegmentCount());
        }

        EdgeInvocation edgeInvocation = createEdgeInvocation(context);
        if (urlDetail.getVersionRule() != null) {
            edgeInvocation.setVersionRule(urlDetail.getVersionRule());
        }

        edgeInvocation.init(urlDetail.getMicroServiceName(), context, path, httpServerFilters);
        logger.info("Matching with url dispatcher,the path {} pattern with {} is router to {} begin,the real path is {}! ", context.request().path(), urlDetail.getPath(), urlDetail.getMicroServiceName(), path);
        edgeInvocation.edgeInvoke();
        logger.info("Router url {} to {} complete", context.request().path(), urlDetail.getMicroServiceName());
    }

    private UrlDetail findConfigurationItem(String path) {
        for (UrlDetail item : GlobalRouterParams.GLOBAL_URL_ROUTER.getUrlDetails()) {
            if (item.getPattern().matcher(path).matches()) {
                return item;
            }
        }
        return null;
    }

    private void loadConfigurations() {
        GlobalRouterParams.loadUrlValues();
    }

    @Override
    public int getOrder() {
        return 6;
    }

    @Override
    public boolean enabled() {
        return DynamicPropertyFactory.getInstance().getBooleanProperty(KEY_ENABLED, false).get();
    }

}
